#charset "us-ascii"
#include "advlite.h"
/*
* ***************************************************************************
* lister.t
*
* This module forms part of the adv3Lite library (c) 2012-13 Eric Eve, and is
* substantially borrowed from the Mercury library (c) 2012 Michael J.
* Roberts.
*/
/*
* Lister is the class that displays lists of objects. This is used in
* room descriptions, inventory lists, and EXAMINE descriptions of
* objects (to show the examined object's contents).
*
* Showing a listing is basically a function call. The reason we make a
* whole class out of it is that we provide a number of options, and a
* class is a convenient way to specify options. The options are simply
* defined as properties of a lister object, so to create a certain kind
* of list, you just set up a Lister instance with the desired options.
* We provide pre-defined Lister objects for the common library listing
* types, but games can create their own custom list types by creating
* their own Lister objects for different sets of options.
*/
class Lister: object
/*
* Show a list of objects. 'lst' is the list of objects to show;
* 'paraCnt' is the number of paragraph-style descriptions that we've
* already shown as part of this description.
*
* Note that many specific listers replaced the 'paraCnt' parameter with a
* more useful 'parent' parameter, containing the identity of the object
* whose contents are being listed.
*/
show(lst, paraCnt, paraBrk = true)
{
/* get the subset that passes our 'listed' test */
lst = lst.subset({ o: listed(o) });
/* if we have any items, show them */
if (lst.length() > 0)
{
/* sort into listing order */
lst = lst.sort(SortAsc, { a, b: listOrder(a) - listOrder(b) });
/*
* The list is plural if it has multiple items, or a single item
* that has plural usage.
*/
local pl = (lst.length() > 1 || lst[1].plural);
/* Show the list prefix */
showListPrefix(lst, pl, paraCnt);
/* show the items */
showList(lst, pl, paraCnt);
/* Show the list suffix. */
showListSuffix(lst, pl, paraCnt);
/* add a paragraph break at the end, if it's requested */
if(paraBrk)
"<.p>";
}
else
showListEmpty(paraCnt);
}
showListPrefix(lst, pl, paraCnt) { }
showListSuffix(lst, pl, paraCnt) { }
showListEmpty(paraCnt) { }
/*
* Should 'obj' be listed in this list? Returns true if so, nil if not.
* By default, we list any object whose 'listed' property is true.
*/
listed(obj) { return obj.listed; }
/*
* Get an item's sorting order. This returns an integer that gives the
* relative position in the list; we order the list in ascending order of
* this value. By default, we return the 'listOrder' property of the
* object.
*/
listOrder(obj) { return obj.listOrder; }
/*
* Return a string containing what this lister would display, minus the
* terminating paragraph break.
*/
buildList(lst)
{
local str = gOutStream.captureOutput({: show(lst, 0, nil) });
return str;
}
;
/*
* An Item Lister is a lister used for listing physical items. Notice that
* most of the specifics of the listers defined below are language-specific,
* and so are defined in the language-specific part of the library (e.g. in
* english.t).
*/
class ItemLister: Lister
/*
* Show a list of objects. 'lst' is the list of objects to show; 'parent'
* parameter is the object whose contents are being listed, 'paraBrk'
* defines whether or not we want a paragraph break after the list.
*/
show(lst, parent, paraBrk = true)
{
/* Carry out the inherited handling */
inherited(lst, parent, paraBrk);
/* Note that every item in our list has been mentioned and seen, provided it's listed. */
foreach(local cur in lst)
{
if(listed(cur))
{
cur.mentioned = true;
cur.noteSeen();
}
}
}
showList(lst, pl, parent)
{
lst = findListGroups(lst);
/*
* If our groupTab table is empty, there are no ListGroups involved in our list, so we can
* just show a simple list of items.
*/
if(groupTab.getEntryCount() == 0)
showSimpleList(lst);
/* Otherwise we'll need to use the more commplex list handling. */
else
showComplexList(lst, pl, parent);
}
/*
* Show a simple list of things. This must be defined in the language-specific part of the
* library.
*/
showSimpleList(lst) { }
/* Our LookUp table of any ListGroups we need to work with. */
groupTab = nil
findListGroups(lst)
{
/* Create a new LookupTable for our table of ListGroups */
groupTab = new LookupTable;
/*
* For every item we're due to list, if it has any ListGroups listed in its listWith
* property, create or update an entry for that ListGroup and the item it lists in our
* table of ListGroups.
*/
foreach(local item in lst)
{
foreach(local grp in valToList(item.listWith))
groupTab[grp] = valToList(groupTab[grp]) + item;
}
/* Get a list of the groups that have been added to groupTab. */
local grpList = groupTab.keysToList();
/*
* Then remove from the list group table entries for any ListGroups that don't match the
* minimum number of items they should list.
*/
foreach (local grp in grpList)
{
if(groupTab[grp].length < grp.minGroupSize)
groupTab.removeElement(grp);
}
/*
* If there's nothing left in the list group table, there's nothing more to do here, so
* simply return our list of items unchanged.
*/
if(groupTab.getEntryCount() == 0)
return lst;
/*
* Get an updated list of the groups that are left, in descending order of priority and
* the number of items they relate to.
*/
grpList = groupTab.keysToList().sort(SortAsc, {a, b: b.priority + groupTab[b].length -
a.priority - groupTab[a].length});
local listed = [];
/*
* Check for items that appear in more than one ListGroup. Remove their association from
* the groupTab table, and if that leaves a ListGroup with too few items, remove that too.
*/
foreach(local grp in grpList)
{
/* Note the items covered by the current ListGroup grp */
local items = groupTab[grp];
/*
* Remove items from the list that would already have been listed in previous
* ListGroups.
*/
items = items.subset({x: listed.indexOf(x) == nil});
/* Update the groupTab entry for this group with the revised list of items. */
groupTab[grp] = items;
/*
* If that leaves the group with too few items for the group to be used, remove it
* from our group table.
*/
if(items.length < grp.minGroupSize)
groupTab.removeElement(grp);
/* Otherwise, add its items to the list of items that will be listed by ListGroups. */
else
listed = listed.appendUnique(items);
}
/*
* Remove the list of items to be listed by ListGroups from the list of items to be listed
* and add the ListGroups in their place.
*/
lst = lst - listed + groupTab.keysToList();
/* Sort the resulting list in ascending order of listOrder. */
lst = lst.sort(SortAsc, {a, b: a.listOrder - b.listOrder});
/* Return the resulting list. */
return lst;
}
/* Show a list of items that may be a list of Things and ListGroups */
showComplexList(lst, pl, parent)
{
/* Note the length of the list. */
local len = lst.length();
/* Iterate through the list. */
for(local i = 1, local item in lst;; ++ i)
{
/* If we're reaching the final item in the list, prepend ' and ' */
if(i == len && i > 1)
DMsg(list and, ' and ');
/* Note the punctuation that should follow this item. */
local punct = ', ';
/* If the item is a Thimg, simpyl display its listName. */
if(item.ofKind(Thing))
{
"<<listName(item)>>";
}
/*
* If it's a ListGroup, get the ListGroup to list its contents and then change the
* following punctuation to a semicolon.
*/
if(item.ofKind(ListGroup))
{
item.showList(self, groupTab[item]);
punct = '; ';
}
/* If we haven't reach the end of the list, add the appropriate punctuation. */
if(i < len)
say(punct);
}
}
/*
* The property on a Thing-derived container to test whether its contents
* should be listed when listing with this lister
*/
contentsListedProp = &contentsListed
/*
* Flag, so we want to list contents of contents when using this lister;
* by default we do.
*/
listRecursively = (gActor == gPlayerChar)
;
/*
* lookLister displays a list of miscellaneous objects in a room description.
*/
lookLister: ItemLister
/* is the object listed in a LOOK AROUND description? */
listed(obj) { return obj.lookListed && !obj.isHidden; }
;
/*
* lookContentsLister is used to display a list of the contents of objects in
* a room description.
*/
lookContentsLister: ItemLister
/* is the object listed in a LOOK AROUND description? */
listed(obj) { return obj.lookListed && !obj.isHidden; }
contentsListedProp = &contentsListedInLook
;
/*
* inventoryLister displays an inventory listing in WIDE format.
*/
inventoryLister: ItemLister
/* is the object listed in an inventory list? */
listed(obj) { return obj.inventoryListed && !obj.isHidden; }
;
/* wornLister displays a list of items being worn. */
wornLister: ItemLister
/* is the object listed in an inventory list? */
listed(obj) { return obj.inventoryListed && !obj.isHidden; }
;
/*
* inventoryTallLister for displaying an inventory list in TALL format.
*/
inventoryTallLister: ItemLister
/* is the object listed in an inventory list? */
listed(obj) { return obj.inventoryListed && !obj.isHidden; }
showList(lst, parent, paraBrk = true)
{
/* list the inventory using the inventory tall format. */
showContentsTall(lst, parent, paraBrk);
/* Ensure the indentation level is reset to zero once we've finished listing */
indentLevel = 1;
}
/*
* List the player's inventory in tall format, i.e., as a columnar list with each item on a
* new line. This method may call itself recursively to list subcontents (such as the visible
* contents of any containers in the player character's inventory).
*/
showContentsTall(lst, parent, paraBrk = true)
{
foreach(local cur in lst)
{
/* Carry out the indenting for sublisting contents */
for(local i in 1..indentLevel)
"\t";
/* Display the appropriate name for the listed item */
say(listName(cur));
/* Move to a new line */
"\n";
/* Note that every item in our list has been mentioned and seen */
cur.mentioned = true;
cur.noteSeen();
/*
* If we want to list recursively and we haven't yet reached out maximum indentation
* (i.e., nesting) level, then build a list of subcontents and then display it.
*/
if(listRecursively && indentLevel < maxIndentLevel)
{
/*
* Get a list of the current item's listable contents. If we can't see in this is
* an empty list.
*/
local subList = (cur.contType == In && !cur.canSeeIn)
? [] : cur.contents.subset({o: listed(o) });
/* If we have an open or transparent subcontainer, add its contents. */
if(cur.remapIn && cur.remapIn.canSeeIn)
subList += cur.remapIn.contents.subset({o: listed(o) });
/* If we have a subsurface, add its contents. */
if(cur.remapOn)
subList += cur.remapOn.contents.subset({o: listed(o) });
/*
* If this list isn't empty, then display this list of subcontents as a column of
* items indented under their containing item.
*/
if(subList.length > 0)
{
/* increment the indentation level. */
indentLevel++;
/* sort the list of subcontents in ascending order of their listOrder property */
subList = subList.sort(true, {x, y: y.listOrder - x.listOrder});
/* call this method recursively to list the subcontents. */
showContentsTall(subList, cur, paraBrk);
/* decrement the indentation level once we've finished listing. */
indentLevel-- ;
}
}
}
}
/*
* A version of the listName method that doesn't list an items contents in parenthesis after
* its name, which would be inappropriate to the tall inventory format.
*/
listName(o)
{
/*
* When we're doing a tall inventory listing we don't want to list sucontents after the
* name of each item, so we store the current value of showSubListing, then set
* showSublisting to nil before carrying out the inherited handling, and then finally
* restore the original value of ShowSubListing.
*/
local ssl = showSubListing;
showSubListing = nil;
local lnam = inherited(o);
showSubListing = ssl;
return lnam;
}
/* The current indentation level for listing subcontents recursively */
indentLevel = 1
/* The maximum level of indentation we want to allow for listed nested subcontents. */
maxIndentLevel = 5
/*
* The property on a Thing-derived container to test whether its contents
* should be listed when listing with this lister
*/
contentsListedProp = &contentsListed
/*
* Flag, so we want to list contents of contents when using this lister;
* by default we do if the actor is the player character.
*/
listRecursively = (gActor == gPlayerChar)
showListPrefix(lst, pl, paraCnt) { DMsg(list tall prefix, '\n{I} {am} carrying:\n '); }
showListSuffix(lst, pl, paraCnt) { }
showListEmpty(paraCnt) { DMsg(list tall empty, '\n{I} {am} empty-handed. '); }
;
/*
* descContentsLister displays a list of miscellaneous contents of an object
* being examined.
*/
descContentsLister: ItemLister
/* is the object listed in an EXAMINE description of its container? */
listed(obj) { return obj.examineListed && !obj.isHidden; }
contentsListedProp = &contentsListedInExamine
;
/*
* openingContentsLister displays the contents of an openable container when
* it is first opened.
*/
openingContentsLister: ItemLister
/* is the object listed in an EXAMINE description of its container? */
listed(obj) { return obj.examineListed && !obj.isHidden; }
/*
* We don't want recursive listing with the openingContentsLister, since
* this can produce odd results.
*/
listRecursively = nil
;
/*
* lookInLister is used to list the contents of an object in response to LOOK
* IN/UNDER/BEHIND
*/
lookInLister: ItemLister
/*
* is the object listed in a SEARCH/LOOK IN/UNDER/BEHIND description of
* its container?
*/
listed(obj) { return obj.searchListed && !obj.isHidden; }
contentsListedProp = &contentsListedInSearch
;
/* A lister used to list the items attached to a SimpleAttachable */
simpleAttachmentLister: ItemLister
/* an object is listed if it's attached */
listed(obj) { return obj.attachedTo != nil && !obj.isHidden; }
;
/* A lister used to list the items plugged into a PlugAttachable */
plugAttachableLister: simpleAttachmentLister
;
/*
* A lister that can be readily customized to tailor the text before and after
* a list of miscellaneous items in a room description.
*/
class CustomRoomLister: ItemLister
/* is the object listed in a LOOK AROUND description? */
listed(obj) { return obj.lookListed && !obj.isHidden; }
/*
* In the simple form of the constructor, we just supply a string that
* will form the prefix string for the lister. In the more sophisticated
* form we can supply an additsion argument that's an anonymous method or
* function that's used to show the list prefix or suffix, or else just
* the suffix string.
*/
construct(prefix, prefixMethod:?, suffix:?, suffixMethod:?)
{
prefix_ = prefix;
if(prefixMethod != nil)
setMethod(&showListPrefix, prefixMethod);
if(suffix != nil)
suffix_ = suffix;
if(suffixMethod != nil)
setMethod(&showListSuffix, suffixMethod);
}
prefix_ = nil
suffix_ = '. '
showListPrefix(lst, pl, irName)
{
"<.p><<prefix_>> ";
}
showListSuffix(lst, pl, irName)
{
"<<suffix_>>";
}
showSubListing = (gameMain.useParentheticalListing)
;
/*
* -----------------------------------------------------------------------------------
*
* Various types of ListGroup
*
* These can be defined on the listWith property of miscellaneous items to be listed in a room,
* container or inventory listing to group items together.
*/
/*
* Abstract base class for all kinds of ListGroup. Game code will use one of more of its
* subclasses defined below.
*/
class ListGroup: object
/*
* The minimum number of objects in the list to be listed belonging to this ListGroup for this
* ListGroup to be used to list them. By default this is two.
*/
minGroupSize = 2
/* Show a list of items (lst) using lister. */
showList(lister, lst)
{
showSimpleList(lister, lst);
}
/*
* Show a simple list of items (lst) using lister. The method of doing so must be provided in
* the language-dependent part of the libary.
*/
showSimpleList(lister, lst) {}
/*
* Our own list order, relative to the listOrder of other items in the list to be listed that
* do not belong to this ListGroup.
*/
listOrder = 100
/*
* Normally if an item to be listed belongs to more than one ListGroup, we use the ListGroup
* that would contain the largest number of items. We can override this by assigning a higher
* priority to the ListGroup so that the highet priority ListGroup will be use. This should be
* a number that's large compared with the number of items in any one group so we use a
* default of 100. Game code may wish to assign priorities in steps of a hundred.
*/
priority = 100
;
/* A ListGroupSorted simply lists its items in ascending listOrder sequence within the group. */
class ListGroupSorted: ListGroup
/*
* The compareGroupItems method defines how items will be sorted within the ListGroup. By
* default we sort on their groupOrder, which by default is the same as their listOrder, but
* we could define an alternative custom property or some other means of sorting if we so
* wished.
*/
compareGroupItems (a, b) { return a.groupOrder - b.groupOrder; }
/* Show our list (lst) of items using lister. */
showList(lister, lst)
{
/* First sort our items. */
lst = sortList(lst);
/* Then show a simple list of them using lister. */
showSimpleList(lister, lst);
}
/* Sort our list of items. By default we do so in their listOrder order. */
sortList(lst) { return lst.sort(SortAsc, {a, b: compareGroupItems(a, b)}); }
;
/*
* ListGroupSuffixPrefix is a type of ListGroupSorted to which we can prepend or append custom
* text, e.g. "keys: "
*/
class ListGroupSuffixPrefix: ListGroupSorted
/*
* The prefix text we want to display before our list of items, if any. This is for game code
* to define.
*/
groupPrefix = nil
/*
* The suffix text we want to display after our list of items, if any. This is for game code
* to define.
*/
groupSuffix = nil
/*
* Show our group prefix. By default we display a apelled out count of the number of items
* this group is going to list followed by our prefix text, but game code can override if some
* other format is wanted.
*/
showGroupPrefix(pov, lst)
{
/* Spell out the number of items we're about to list. */
"<<spellNumber(lst.length)>> ";
/* Then display our group prefix/ */
display(&groupPrefix);
}
/* Display our group suffix. */
showGroupSuffix(pov, lst) { display(&groupSufffix); }
/* Show our list of items (lst) using lister. */
showList(lister, lst)
{
/* Display our group prefix. */
showGroupPrefix(gActor, lst);
/* Sort out list */
lst = sortList(lst);
/* Output a apace to separate the list from our prefix. */
" ";
/* Show a simple list of our items (lst) using lister. */
showSimpleList(lister, lst);
/* Output another space to separate the list from its suffix */
" ";
/* Show the group suffix. */
showGroupSuffix(gActor, lst);
}
;
/*
* ListGroupParem introduces the list with a summary of what it contains (e.g., 'three keys') and
* then lists its individual items in parentheses (e.g. 'three keys (a large key, a brass key, and
* a tiny silver key).
*/
class ListGroupParen: ListGroupSorted
/* Show the summmary prefix, e.g. "three keys". */
showGroupCountName(lst)
{
"<<spellNumber(lst.length)>> <<pluralName>>";
}
/* The plural name to use to summarise the items in the group, e.g., 'keys' */
pluralName = ''
/* Show our list of items (lst) using lister. */
showList(lister, lst)
{
/* Start with our summary description, e.g., 'three keys' */
showGroupCountName(lst);
/* Sort our list of items. */
lst = sortList(lst);
/* Display the opening parenthesis. */
" (";
/* Show a simple list of our itmes (lst) using lister. */
showSimpleList(lister, lst);
/* Display the closing parenthesis. */
")";
}
;
/* A ListGroupCuatom displays the list or a summary of the list in a user-defined way, */
class ListGroupCustom: ListGroup
/* The text to display to define this group. Tbis must be defined in user code. */
showGroupMsg(lst) { }
/* Show our list. */
showList(lister, lst)
{
/* Call showGroupMsg(lst) to do the work of displaying it. */
showGroupMsg(lst);
}
;
Adv3Lite Library Reference Manual
Generated on 13/06/2025 from adv3Lite version 2.2.1